#include "gtd_common.hpp"
#include "gtd_video.hpp"
#include "gtd_profiler.hpp"
#ifdef _WIN32
#include <windows.h>
#include <vfw.h>
#endif

NAMESPACE_BEG(gtd)

VideoSource::VideoSource(): 
width(0),
height(0)
{
}

VideoSource::~VideoSource()
{
}

int VideoSource::getWidth() const
{
    return width;
}

int VideoSource::getHeight() const
{
    return height;
}

struct AviFile::Data
{
#ifdef _WIN32
    PAVIFILE file;
    AVIFILEINFO finfo;
    PAVISTREAM stream;
    AVISTREAMINFO sinfo;
    BITMAPINFO format;
    PGETFRAME pgf;
    int curFrame;
#endif
};

AviFile::AviFile(const std::string& filename):
data(new Data()), d_frame(NULL)
{
    std::memset(data, sizeof(Data), 0);

#ifdef _WIN32
    HRESULT status;

    AVIFileInit();

    status = AVIFileOpen(&data->file, filename.c_str(), OF_READ, NULL);
    if(status != S_OK)
    {
        throw GTD_ERROR("AVIFileOpen " + toString(status));
    }

    status = AVIFileInfo(data->file, &data->finfo, sizeof(data->finfo));
    if(status != S_OK)
    {
        throw GTD_ERROR("AVIFileInfo " + toString(status));
    }

    status = AVIFileGetStream(data->file, &data->stream, streamtypeVIDEO, 0);
    if(status != S_OK)
    {
        throw GTD_ERROR("AVIFileGetStream " + toString(status));
    }

    status = AVIStreamInfo(data->stream, &data->sinfo, sizeof(data->sinfo));
    if(status != S_OK)
    {
        throw GTD_ERROR("AVIStreamInfo " + toString(status));
    }

    LONG size = sizeof(data->format);
    status = AVIStreamReadFormat(data->stream, AVIStreamStart(data->stream), 
        &data->format, &size);
    if(status != S_OK)
    {
        throw GTD_ERROR("AVIStreamReadFormat " + toString(status));
    }

    width = data->format.bmiHeader.biWidth;
    height = data->format.bmiHeader.biHeight;

    data->format.bmiHeader.biBitCount = 32;
    data->format.bmiHeader.biSizeImage = width * height * 4;
    data->format.bmiHeader.biCompression = 0;

    data->pgf = AVIStreamGetFrameOpen(data->stream, &data->format.bmiHeader);
    if(data->pgf == NULL)
    {
        throw GTD_ERROR("AVIStreamGetFrameOpen");
    }
#endif
}

AviFile::~AviFile()
{
    CUDA_ERROR_CHECK(cudaFree(d_frame));

#ifdef _WIN32
    HRESULT status;

    status = AVIStreamGetFrameClose(data->pgf);
    if(status != S_OK)
    {
        std::cerr << GTD_ERROR("AVIStreamGetFrameClose " 
            + toString(status)).what();
    }

    status = AVIStreamRelease(data->stream);
    if(status != S_OK)
    {
        std::cerr << GTD_ERROR("AVIStreamRelease " 
            + toString(status)).what();
    }

    status = AVIFileRelease(data->file);
    if(status != S_OK)
    {
        std::cerr << GTD_ERROR("AVIFileRelease " 
            + toString(status)).what();
    }

    AVIFileExit();
#endif
    delete data;
}

const uchar4* AviFile::getFrame()
{
    PROFILE("Frame Grabbing");

#ifdef _WIN32
    if(data->curFrame < (int)data->sinfo.dwLength)
    {
        if(d_frame == NULL)
        {
            CUDA_ERROR_CHECK(cudaMalloc(&d_frame, width * height * 4));
        }

        void* bytes = AVIStreamGetFrame(data->pgf, data->curFrame);
        LPBITMAPINFOHEADER header = (LPBITMAPINFOHEADER)bytes;
        
        CUDA_ERROR_CHECK(cudaMemcpy(d_frame, (char*)bytes + header->biSize,
            width * height * 4, cudaMemcpyHostToDevice));

        ++data->curFrame;
        return d_frame;
    }
#endif
    return NULL;
}

GenVideo::GenVideo():
d_frame(NULL)
{
    width = 512;
    height = 512;
}

GenVideo::~GenVideo()
{
    CUDA_ERROR_CHECK(cudaFree(d_frame));
}

GLint faces[6][4] = {  /* Vertex indices for the 6 faces of a cube. */
  {0, 1, 2, 3}, {3, 2, 6, 7}, {7, 6, 5, 4},
  {4, 5, 1, 0}, {5, 6, 2, 1}, {7, 4, 0, 3} };

const uchar4* GenVideo::getFrame()
{
    PROFILE("Frame Generating");

 //   if(d_frame == NULL)
 //   {
 //       CUDA_ERROR_CHECK(cudaMalloc(&d_frame, width * height * 4));
 //       frame.resize(width * height);
 //   }

 //   GL_ERROR_CHECK(glClear(GL_COLOR_BUFFER_BIT));

 //   static float rquad = 0.0f;
 //   glLoadIdentity();
 //   glRotatef(rquad,1.0f,1.0f,1.0f);

 //   glColor3f(1.0f, 1.0f, 1.0f);

 //   glBegin(GL_QUADS);

 //   glColor3f(0.0f,1.0f,0.0f);			// Set The Color To Green
	//glVertex3f( 1.0f, 1.0f,-1.0f);			// Top Right Of The Quad (Top)
	//glVertex3f(-1.0f, 1.0f,-1.0f);			// Top Left Of The Quad (Top)
	//glVertex3f(-1.0f, 1.0f, 1.0f);			// Bottom Left Of The Quad (Top)
	//glVertex3f( 1.0f, 1.0f, 1.0f);	

 //   glEnd();
 //   
 //   GL_ERROR_CHECK(glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, &frame[0]));
 //   GL_ERROR_CHECK(glFinish());

 //   CUDA_ERROR_CHECK(cudaMemcpy(d_frame, &frame[0],
 //       width * height * 4, cudaMemcpyHostToDevice));

 //   rquad += 0.15f;

    return d_frame;
}

NAMESPACE_END(gtd)
